#pragma once
#include <string>

#include "Constant.hpp"
#include "Transformation.hpp"

/**
 * @brief 晨昏蒙影 项
 *        =>日出前或日没后由高空大气散射太阳光引起的天空发亮的现象。
 *
 * 地平(horizon) 晨昏 => HourAngle(50');
 * 民用(civil)   晨昏 => HourAngle(06°);
 * 航海(nautical)晨昏 => HourAngle(12°);
 * 天文(astronomical)晨昏 => HourAngle(18°);
 */
struct TwilightEntry
{
    // 时角;
    double HourAngle;
    // 黎明;
    double Dawn;
    // 黄昏;
    double Dusk;
};
/**
 * @brief 日/月 晨昏时间信息;
 *
 */
class Twilight
{

public:
    enum class Type : unsigned char
    {
        // 地平线;
        Horizon,
        // 民用;
        Civil,
        // 航海;
        Nautical,
        // 天文;
        Astronomical,
    };

#define  ALL  ((int)Type::Astronomical + 1)

public:
    Twilight(double jd) : Rise(jd), Set(jd), Transit(jd), LowerTransit(jd),Description("")
    {
        for (int i = 0; i < ALL ; i++)
        {
            auto entry  = &this->TwilightEntry[i];

            entry->Dawn = jd;
            entry->Dusk = jd;
        }
    }
    /**
     * @brief 计算晨昏时间;
     * 
     * @param type 
     *         ;
     * @param sv 
     *         转换值;
     */
    void CalcEntry(Type type, const double sv)
    {
        auto entry = &this->TwilightEntry[(int)type];

        entry->Dawn += (-entry->HourAngle - this->H) / sv;
        entry->Dusk += ( entry->HourAngle - this->H) / sv;
    }

    /**
     * @brief 太阳光照时间长度;
     * 
     * @return double 
     */
    double GetSunlight()
    {
        return this->Set - this->Rise - 0.5;
    }
    /**
     * @brief 昼长;
     * 
     * @return double 
     */
    double GetDayLength()
    {
        auto e = &this->GetEntry(Type::Civil);
        return   e->Dusk - e->Dawn - 0.5;
    }
    /**
     * @brief 设置某类型时角;
     * 
     * @param type 
     *         类型;
     * @param value 
     *         需要设置的数值;
     */
    void SetHourAngle(Type type, double value) { this->TwilightEntry[(int)type].HourAngle = value; }

    /**
     * @brief 获取指定项;
     * 
     * @param type 
     *         类型;
     * @return TwilightEntry&
     */
    TwilightEntry &GetEntry(const Type type) { return this->TwilightEntry[int(type)]; }

    /**
     * @brief 增加描述;
     * 
     * @param des 
     */
    void AddDescription(std::string des)
    {
        this->Description += des;
    }
    /**
     * @brief 获取描述文本;
     * 
     * @return std::string 
     */
    std::string GetDescription() const
    {
        return this->Description;
    }
public:
    // s , 升起;
    double Rise;
    // z , 中天;
    double Transit;
    // x , 下中天;
    double LowerTransit;
    // j , 降落;
    double Set;
    // 此刻天体时角;
    double H;

private:
    TwilightEntry TwilightEntry[ALL];

    // 描述;
    std::string   Description;
;
};

/**
 * @brief 太阳，月亮 升降计算;
 * 
 */
class RiseTransitSet
{
public:
    RiseTransitSet(double l, double fa) : Lon(l), Lat(fa), dt(0), E(0.409092614) {}

public:
    /**
     * @brief 获取时角;
     *
     * @param h 地平纬度;
     * @param w 赤纬;
     * @return double 时角
     */
    double CalcHourAngle(double h, double w);

    /**
     * @brief //章动同时影响恒星时和天体坐标,所以不计算章动。
     *
     * @param jd 当地中午12点时间对应的2000年首起算的格林尼治时间UT;
     * @param H0 是否计算升起对应的时角;
     * @param r  返回时角及赤经纬;
     */
    void Mcoord(double jd, bool H0, Twilight &r);

    /**
     * @brief 月亮到中升降时刻计算;
     *
     * @param jd 当地中午12点时间对应的2000年首起算的格林尼治时间UT;
     * @return Twilight 升降时刻对象;
     */
    Twilight Mt(double jd);

    /**
     * @brief //章动同时影响恒星时和天体坐标,所以不计算章动。
     *
     * @param jd 当地中午12点时间对应的2000年首起算的格林尼治时间UT;
     * @param xm 计算指定时角[
                        10 =>表示所有晨昏线(默认)，
                        1  =>地平以下50分，
                        2  =>地平以下6度，
                        3  =>地平以下12度,
                        4  =>地平以下18度,
                        其他=>则表示不计算时角];
     * @param r 返回时角及赤经纬;
     */
    void Scoord(double jd,int xm, Twilight &r);

    /**
     * @brief 太阳到中升降时刻计算;
     *
     * @param jd 当地中午12点时间对应的2000年首起算的格林尼治时间UT;
     * @return Twilight 升降时刻对象;
     */
    Twilight St(double jd);

private:

    /**
     * @brief 获取注释;
     * 
     * @param type 
     *         类型;
     * @param Set 
     *         晨(False)/昏(True)
     * @return std::string 
     */
    std::string GetDescription(Twilight::Type type, bool Set)
    {
        std::string des[] = {"无升起.","无降落.","无民用晨.","无民用昏.","无航海晨.","无航海昏.","无天文晨.","无天文昏."};
        return des[int(type) + int(Set)];
    }

    /**
     * @brief 计算St 单个中的项目;
     * 
     * @param type 
     *        类型;
     * @param i 
     *         Scoord 中的XM;
     * @param r 
     *        Twilight 对象;
     */
    void StEntry(Twilight::Type type, int i, Twilight &r)
    {
        auto entry = &r.GetEntry(type);

        this->Scoord(entry->Dawn, i, r);
        entry->Dawn += Transformation::MapToNegPIToPIRange(-entry->HourAngle - r.H) / Constants::PI2;
        if(entry->HourAngle == Constants::PI)
            r.AddDescription(GetDescription(type, false));
        this->Scoord(entry->Dusk, i, r);
        entry->Dusk += Transformation::MapToNegPIToPIRange(+entry->HourAngle - r.H) / Constants::PI2;
        if(entry->HourAngle == Constants::PI)
            r.AddDescription(GetDescription(type, true));
    }
    // 多天升中降计算,jd是当地起始略日(中午时刻),sq是时区;
    static void calcRTS(double jd, int n, double Jdl, double Wdl, double sq)
    {

    }
private:
    // 站点地理经度,向东测量为正;
    double Lon;
    // 站点地理纬度;
    double Lat;
    // TD-UT;
    double dt;
    // 黄赤交角;
    double E;
};